#include "subprocess.h"
#include <stdio.h>

int subprocess_init(struct subprocess* p)
{
    SECURITY_ATTRIBUTES saAttr;

    // Set the bInheritHandle flag so pipe handles are inherited.

    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    p->stdin_rd = NULL;
    p->stdin_wr = NULL;
    p->stdout_rd = NULL;
    p->stdout_wr = NULL;
    p->process = NULL;

    if (!CreatePipe(&p->stdout_rd, &p->stdout_wr, &saAttr, 0))
    {
        goto error;
    }

    // Ensure the read handle to the pipe for STDOUT is not inherited.

    if (!SetHandleInformation(p->stdout_rd, HANDLE_FLAG_INHERIT, 0))
    {
        goto error;
    }

    // Create a pipe for the child process's STDIN.

    if (! CreatePipe(&p->stdin_rd, &p->stdin_wr, &saAttr, 0))
    {
        goto error;
    }

    // Ensure the write handle to the pipe for STDIN is not inherited.

    if (!SetHandleInformation(p->stdin_wr, HANDLE_FLAG_INHERIT, 0))
    {
        goto error;
    }

    return 0;

error:

    subprocess_close(p);
    return -1;
}

int subprocess_start(struct subprocess* p, wchar_t* cmd)
{
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    BOOL bSuccess = FALSE;

    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));

    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = p->stdout_wr;
    siStartInfo.hStdOutput = p->stdout_wr;
    siStartInfo.hStdInput = p->stdin_rd;
    siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    siStartInfo.wShowWindow = SW_HIDE;

    bSuccess = CreateProcess(NULL,
                             cmd,           // command line
                             NULL,          // process security attributes
                             NULL,          // primary thread security attributes
                             TRUE,          // handles are inherited
                             0,             // creation flags
                             NULL,          // use parent's environment
                             NULL,          // use parent's current directory
                             &siStartInfo,  // STARTUPINFO pointer
                             &piProcInfo);  // receives PROCESS_INFORMATION

    if (! bSuccess)
    {
        return -1;
    }

    p->process = piProcInfo.hProcess;
    CloseHandle(piProcInfo.hThread);
    return 0;
}

int subprocess_wait(struct subprocess* p)
{
    DWORD wait;
    wait = WaitForSingleObject(p->process, INFINITE);
    return wait == WAIT_OBJECT_0 ? 0 : -1;
}

int subprocess_exited(struct subprocess* p)
{
    DWORD wait;
    wait = WaitForSingleObject(p->process, 0);
    return wait == WAIT_OBJECT_0 ? 0 : -1;
}

int subprocess_write(struct subprocess* p, const char* text)
{
    BOOL b;
    DWORD dwWritten;

    b = WriteFile(p->stdin_wr, text, strlen(text), &dwWritten, NULL);
    return b ? dwWritten : -1;
}

int subprocess_read(struct subprocess* p, char* text, int max_size)
{
    DWORD dwRead;
    BOOL b = FALSE;

    b = ReadFile(p->stdout_rd, text, max_size, &dwRead, NULL);
    text[dwRead] = 0;
    return b ? dwRead : -1;
}

int subprocess_close(struct subprocess* p)
{
    int ret = 0;

    if (p->stdin_wr && !CloseHandle(p->stdin_wr))
    {
        ret = -1;
    }

    if (p->stdin_rd && !CloseHandle(p->stdin_rd))
    {
        ret = -1;
    }

    if (p->stdout_wr && !CloseHandle(p->stdout_wr))
    {
        ret = -1;
    }

    if (p->stdout_rd && !CloseHandle(p->stdout_rd))
    {
        ret = -1;
    }

    if (p->process && !CloseHandle(p->process))
    {
        ret = -1;
    }

    p->stdin_wr = NULL;
    p->stdin_rd = NULL;
    p->stdout_wr = NULL;
    p->stdout_rd = NULL;
    p->process = NULL;

    return ret;
}

